import {
  CheckCircleOutlined,
  CheckOutlined,
  DeleteFilled,
  DeleteRowOutlined,
  EditOutlined,
  ExpandAltOutlined,
  LoadingOutlined,
  PlusOutlined,
  RollbackOutlined,
} from "@ant-design/icons"
import { data, storage, store } from "@mall/api-services"

import { Button, Checkbox, Divider, Empty, Form, Input, Pagination, Popover, Spin, Tree, Typography } from "antd"
import classNames from "classnames"
import * as _ from "lodash"
import * as React from "react"
import { useCallback, useEffect, useRef, useState } from "react"
import Dropzone from "react-dropzone"
import { useParams } from "react-router-dom"
import classes from "./image-blobs.module.scss"

const { DirectoryTree } = Tree

const DragUploadImageSvg = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
  <path
    d="M69.81 124.43a1.39 1.39 0 01-.57-.12 1.43 1.43 0 01-.49-.32 1.52 1.52 0 01-.44-1.06 1.5 1.5 0 01.44-1.06 1.53 1.53 0 011.35-.42 1.31 1.31 0 01.28.09 1.08 1.08 0 01.26.14 1.06 1.06 0 01.23.19 1.37 1.37 0 01.19.22 1.26 1.26 0 01.13.26 1.49 1.49 0 01-.32 1.64 1.34 1.34 0 01-.49.32 1.39 1.39 0 01-.57.12zm-9.71-1.5a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.51 1.51 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm-8.22 0a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm-8.22 0a1.5 1.5 0 011.5-1.5 1.51 1.51 0 011.51 1.5 1.51 1.51 0 01-1.51 1.5 1.51 1.51 0 01-1.5-1.5zm-8.21 0a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm-8.22 0a1.5 1.5 0 011.5-1.5 1.51 1.51 0 011.51 1.5 1.51 1.51 0 01-1.51 1.5 1.51 1.51 0 01-1.5-1.5zm-8.21 0a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.52-1.5zm-8.22 0a1.5 1.5 0 011.5-1.5 1.51 1.51 0 011.51 1.5 1.51 1.51 0 01-1.51 1.5 1.5 1.5 0 01-1.5-1.5zm-6.71 1.5A1.5 1.5 0 013 124a1.47 1.47 0 01-.44-1.06 1.53 1.53 0 010-.3c0-.09 0-.19.08-.28a2.15 2.15 0 01.14-.26 1.37 1.37 0 01.19-.22 1.06 1.06 0 01.23-.19 1.08 1.08 0 01.26-.14 1.31 1.31 0 01.28-.09 1.88 1.88 0 01.58 0 1.31 1.31 0 01.28.09 1.3 1.3 0 01.26.14 1.37 1.37 0 01.23.19 1.37 1.37 0 01.19.22 1.26 1.26 0 01.13.26 1.41 1.41 0 01.12.58 1.56 1.56 0 01-.11.57 1.63 1.63 0 01-.33.49 1.5 1.5 0 01-1 .43zm-1.5-9.5a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.49zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.49zm0-8a1.5 1.5 0 011.5-1.5A1.5 1.5 0 015.59 83a1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm0-8a1.5 1.5 0 011.5-1.5A1.5 1.5 0 015.59 75a1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm0-8a1.5 1.5 0 011.5-1.5A1.5 1.5 0 015.59 67a1.51 1.51 0 01-1.5 1.51A1.51 1.51 0 012.59 67zm0-8a1.5 1.5 0 011.5-1.5A1.5 1.5 0 015.59 59a1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm0-8a1.51 1.51 0 011.5-1.51A1.51 1.51 0 015.59 51a1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm1.5-6.46A1.5 1.5 0 012.59 43a1.36 1.36 0 010-.29 2.29 2.29 0 01.08-.28 2.15 2.15 0 01.14-.26 1.57 1.57 0 01.42-.42 2 2 0 01.25-.14l.29-.08a1.5 1.5 0 011.35.41 1.45 1.45 0 01.19.23 1.26 1.26 0 01.13.26 1.31 1.31 0 01.09.28 1.36 1.36 0 010 .29 1.52 1.52 0 01-.38 1 2.11 2.11 0 01-.23.19 1.26 1.26 0 01-.26.13 1.31 1.31 0 01-.28.09 1.43 1.43 0 01-.29.06zM60.1 43a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm-8.22 0a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm-8.22 0a1.5 1.5 0 011.5-1.5 1.51 1.51 0 011.51 1.5 1.51 1.51 0 01-1.51 1.5 1.5 1.5 0 01-1.5-1.5zm-8.21 0a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm-8.22 0a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zM19 43a1.5 1.5 0 011.5-1.5A1.5 1.5 0 0122 43a1.5 1.5 0 01-1.5 1.5A1.5 1.5 0 0119 43zm-8.2 0a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm59.01 1.47a1.5 1.5 0 01-1.06-.47 1.52 1.52 0 01-.44-1 1.5 1.5 0 011.79-1.5l.28.08.26.14a2.11 2.11 0 01.23.19 1.5 1.5 0 01.44 1.09 1.52 1.52 0 01-.44 1 2.11 2.11 0 01-.23.19 1.26 1.26 0 01-.26.13 1.31 1.31 0 01-.28.09 1.36 1.36 0 01-.29.06zm-1.5 70.46a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.5zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.49zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.49zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.43zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.43zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.49 1.5 1.5 0 01-1.5-1.42zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.43zm0-8a1.5 1.5 0 011.5-1.5 1.5 1.5 0 011.5 1.5 1.5 1.5 0 01-1.5 1.5 1.5 1.5 0 01-1.5-1.43z"
    fill="#dee4f5"/>
  <path fill="#fff" d="M26.67 3.57h79.41v96.61H26.67z"/>
  <path
    d="M106.92 101H91.78c-1.64 0-3.24.15-4.9.18s-3.39-.05-5.11-.13c-.85 0-1.7-.06-2.54 0s-1.67 0-2.49 0l-9.91.29c-3.32.08-6.62.24-10 .26s-6.78-.27-10.11-.19c-6.63.42-13.4.16-20.07 0h-.08a1.1 1.1 0 01-1.07-1.13c.08-2 .06-4 .08-6s0-4 0-6-.19-4-.23-6 .15-4 .28-6a21.6 21.6 0 000-3 27.86 27.86 0 01-.14-3v-6l.13-12.08v-12l.33-6a53 53 0 00-.13-6c-.23-4 .12-8 .26-12.08l.06-3v-3l-.08-6A.55.55 0 0126.64 3c3.3-.1 6.61 0 9.92 0s6.62 0 9.93.06c1.65 0 3.31.06 5 .07H54l2.48-.08 9.93-.33C73 2.54 79.6 2.4 86.22 2.43c3.31.07 6.62.3 9.93.24l9.92-.26a1.13 1.13 0 011.16 1.1v.06l.18 6.1c0 2-.11 4.06-.16 6.08 0 4.06.22 8.16.13 12.22 0 2-.26 4-.33 6-.06 1 0 2 0 3v6.06l-.12 3a46.75 46.75 0 00.09 6.13c.21 4.16-.21 8-.2 12.08-.17 4 .13 8.2 0 12.21 0 2-.15 4-.16 6s.07 4.11.13 6.19l.08 3.12v3.06c.06 2.18.13 4.18.05 6.18zm-1.7-1.69V87.37c0-2 .08-3.93.06-5.93s-.2-4.13-.26-6.15c-.15-4.08.22-7.83 0-11.93-.06-2-.16-4.06-.27-6.1a43.89 43.89 0 010-6 31.14 31.14 0 00.16-5.9 44.75 44.75 0 01-.09-6 58.45 58.45 0 00.18-5.86c0-2-.27-4.05-.28-6s0-4 .11-5.95.15-4 .14-6-.19-4-.16-6l.14-6 1.11 1.11c-3.31-.07-6.61-.19-9.92-.18s-6.62.3-9.93.43C79.6 5.06 73 5 66.37 4.83c-1.66 0-3.31-.1-5-.17l-5-.29c-3.31-.18-6.61 0-9.92.13-6.62.22-13.24.39-19.85.27l1.2-1.2-.28 6v3l.1 3c.22 4 .83 8.06.37 12.08-.11 1-.27 2-.34 3s0 2 0 3c.11 2 .41 4 .5 6v3l-.07 3-.14 6L28 64l-.06 6a22 22 0 01-.2 3 20.18 20.18 0 00-.08 3c.09 2 .28 4 .19 6l-.27 6c-.06 4-.14 8.05-.13 12.08l-.74-.75h9.85c3.28 0 6.58.08 9.86.06s6.51-.25 9.76-.29 6.57.1 9.87.14l9.91.19h4.89c1.59 0 3.18-.14 4.82-.14s3.34.11 5 .14l4.86-.06c3.17-.05 6.47-.02 9.69-.04z"
    fill="#fff"/>
  <g opacity=".5" fill="#dee4f5">
    <path d="M26.67 3.57h79.41v96.61H26.67z"/>
    <path
      d="M106.92 101H91.78c-1.64 0-3.24.15-4.9.18s-3.39-.05-5.11-.13c-.85 0-1.7-.06-2.54 0s-1.67 0-2.49 0l-9.91.29c-3.32.08-6.62.24-10 .26s-6.78-.27-10.11-.19c-6.63.42-13.4.16-20.07 0h-.08a1.1 1.1 0 01-1.07-1.13c.08-2 .06-4 .08-6s0-4 0-6-.19-4-.23-6 .15-4 .28-6a21.6 21.6 0 000-3 27.86 27.86 0 01-.14-3v-6l.13-12.08v-12l.33-6a53 53 0 00-.13-6c-.23-4 .12-8 .26-12.08l.06-3v-3l-.08-6A.55.55 0 0126.64 3c3.3-.1 6.61 0 9.92 0s6.62 0 9.93.06c1.65 0 3.31.06 5 .07H54l2.48-.08 9.93-.33C73 2.54 79.6 2.4 86.22 2.43c3.31.07 6.62.3 9.93.24l9.92-.26a1.13 1.13 0 011.16 1.1v.06l.18 6.1c0 2-.11 4.06-.16 6.08 0 4.06.22 8.16.13 12.22 0 2-.26 4-.33 6-.06 1 0 2 0 3v6.06l-.12 3a46.75 46.75 0 00.09 6.13c.21 4.16-.21 8-.2 12.08-.17 4 .13 8.2 0 12.21 0 2-.15 4-.16 6s.07 4.11.13 6.19l.08 3.12v3.06c.06 2.18.13 4.18.05 6.18zm-1.7-1.69V87.37c0-2 .08-3.93.06-5.93s-.2-4.13-.26-6.15c-.15-4.08.22-7.83 0-11.93-.06-2-.16-4.06-.27-6.1a43.89 43.89 0 010-6 31.14 31.14 0 00.16-5.9 44.75 44.75 0 01-.09-6 58.45 58.45 0 00.18-5.86c0-2-.27-4.05-.28-6s0-4 .11-5.95.15-4 .14-6-.19-4-.16-6l.14-6 1.11 1.11c-3.31-.07-6.61-.19-9.92-.18s-6.62.3-9.93.43C79.6 5.06 73 5 66.37 4.83c-1.66 0-3.31-.1-5-.17l-5-.29c-3.31-.18-6.61 0-9.92.13-6.62.22-13.24.39-19.85.27l1.2-1.2-.28 6v3l.1 3c.22 4 .83 8.06.37 12.08-.11 1-.27 2-.34 3s0 2 0 3c.11 2 .41 4 .5 6v3l-.07 3-.14 6L28 64l-.06 6a22 22 0 01-.2 3 20.18 20.18 0 00-.08 3c.09 2 .28 4 .19 6l-.27 6c-.06 4-.14 8.05-.13 12.08l-.74-.75h9.85c3.28 0 6.58.08 9.86.06s6.51-.25 9.76-.29 6.57.1 9.87.14l9.91.19h4.89c1.59 0 3.18-.14 4.82-.14s3.34.11 5 .14l4.86-.06c3.17-.05 6.47-.02 9.69-.04z"/>
  </g>
  <rect x="38.8" y="16.17" width="21.96" height="23.33" rx="10.71" ry="10.71" fill="#dee4f5"/>
  <path
    d="M50.05 40.29a11.78 11.78 0 01-4.7-.82c-.72-.35-1.43-.72-2.15-1.09-.35-.19-.68-.43-1-.64s-.65-.5-1-.76a11.45 11.45 0 01-3.27-8.87L38 27c0-.39 0-.8.07-1.2s.14-.8.22-1.2l.34-1.16a10.68 10.68 0 012.51-4 15.51 15.51 0 011.76-1.58 10.18 10.18 0 012.06-1.16 13.63 13.63 0 014.58-.95 11.85 11.85 0 014.58.81 12.29 12.29 0 014 2.41 11.75 11.75 0 013.46 8.65 12.61 12.61 0 01-3 8.82 7.57 7.57 0 01-.89.81 8.46 8.46 0 01-1 .72 12.42 12.42 0 01-2 1.23 11.14 11.14 0 01-4.64 1.09zm0-1.58a9.69 9.69 0 004-.92 9.32 9.32 0 001.77-1.06 5.12 5.12 0 00.76-.71 7.07 7.07 0 00.67-.78 11.15 11.15 0 002.09-3.49l.3-1 .16-1 .07-1v-1.18c0-.38.05-.76 0-1.1a6.15 6.15 0 000-1 6 6 0 00-.53-2A10.19 10.19 0 0057.05 20c-.51-.47-1.07-.86-1.57-1.36a4.44 4.44 0 00-1.82-1 6.07 6.07 0 00-1-.25 6.14 6.14 0 00-1-.12c-.71-.08-1.33 0-2.11 0h-1a8.61 8.61 0 00-1 .12 9.38 9.38 0 00-2 .68 11.32 11.32 0 00-1.79 1 5 5 0 00-.83.62l-.39.35-.32.4a25.35 25.35 0 00-2.11 3.55 6.48 6.48 0 00-.32 1c-.05.34-.11.69-.15 1L39.46 27l-.1 1.13a9.38 9.38 0 003 7.71c.24.25.5.49.75.74s.54.45.83.66a5.59 5.59 0 001.9.92 11.23 11.23 0 004.21.55z"
    fill="#dee4f5"/>
  <path fill="#fff" d="M49.77 27.84h28.68v30.47H49.77z"/>
  <path
    d="M79 58.87c-2.43.05-5-.13-7.31.07a41.42 41.42 0 00-7.26.18 24.87 24.87 0 01-3.67 0 16 16 0 00-3.67.05c-2.43.16-4.92-.2-7.34-.21a.67.67 0 01-.67-.68v-1.91c0-.64-.1-1.27-.15-1.9-.15-1.27.44-2.54.08-3.81 0-2.54.1-5.08.1-7.62a32.31 32.31 0 01.09-3.81 15.26 15.26 0 00.18-1.9c0-.64-.13-1.27-.16-1.91 0-1.26.15-2.53.23-3.8s0-2.54-.05-3.81a.36.36 0 01.34-.37c1.2-.06 2.39 0 3.59 0s2.39 0 3.58.06c2.39.16 4.78-.23 7.17-.29a86.1 86.1 0 013.59-.13 25.19 25.19 0 013.58.09 63.62 63.62 0 007.2-.17.87.87 0 01.89.83v.06a18 18 0 01-.1 1.94 18 18 0 000 1.94 15.61 15.61 0 01-.12 3.87 38.3 38.3 0 00-.06 3.87 13.2 13.2 0 00-.11 1.9 19.64 19.64 0 01.15 2c-.06 1.27-.22 2.49-.22 3.77s.16 2.69 0 3.91a21.08 21.08 0 000 3.89v2c0 .66.12 1.26.12 1.89zm-1.13-1.13v-3.71a18.64 18.64 0 00-.08-3.78 35.5 35.5 0 010-3.72c0-1.25-.23-2.59-.28-3.84a8.67 8.67 0 01.21-1.78 11.28 11.28 0 00-.12-1.9 8.54 8.54 0 01.1-1.83 7.82 7.82 0 000-1.87 14.21 14.21 0 010-3.74 14.39 14.39 0 00.06-1.86c0-.63-.11-1.25-.09-1.87l.83.83c-2.39 0-4.78-.4-7.17-.08a21.49 21.49 0 01-3.58.19H65.9l-1.79-.07c-2.39 0-4.78-.64-7.17-.29a68.69 68.69 0 01-7.17.22l.8-.8a22.09 22.09 0 00-.16 3.81 22.33 22.33 0 01.37 3.8 16 16 0 00-.35 1.91 8.86 8.86 0 00.26 1.9 16.19 16.19 0 010 3.81c-.11 2.54 0 5.08-.17 7.62-.42 1.27.13 2.54 0 3.81l-.18 1.9-.05 1.91-.53-.53c2.37.09 4.71 0 7.08 0a22.57 22.57 0 003.5-.1 25.48 25.48 0 013.5 0 45 45 0 007.05.06c2.47.17 4.66-.03 7 0z"
    fill="#fff"/>
  <path d="M83.71 41.19L74 59.28l21.46-.67-11.15-17.45a.34.34 0 00-.6.03z" fill="#dee4f5"/>
  <path
    d="M84.4 41.56c-.82 1.5-1.59 3-2.49 4.48l-1 2.37c-.35.78-.75 1.53-1.17 2.28l-2.52 4.47c-.81 1.5-1.67 3-2.54 4.45l-.65-1c1.79 0 3.58.14 5.37 0s3.58 0 5.37-.09 3.57 0 5.36-.12a53.51 53.51 0 015.36-.43l-.55 1-1.49-2.23c-.52-.74-1-1.47-1.52-2.22C91 53 90.13 51.43 89.1 50c-.48-.76-1-1.48-1.45-2.26s-.85-1.58-1.36-2.31-1-1.49-1.51-2.22L84 42.08l-.31-.46a.44.44 0 00.71-.06zm-1.4-.74a1.13 1.13 0 01.85-.6 1.15 1.15 0 011 .36 2 2 0 01.17.24l.09.14.19.28.73 1.12 1.48 2.24c.47.76 1.06 1.44 1.58 2.17s.94 1.52 1.41 2.28c.89 1.56 1.86 3.06 2.78 4.6.44.78.85 1.58 1.34 2.33l1.53 2.2a.78.78 0 01-.2 1.08.75.75 0 01-.38.13h-.06a24.72 24.72 0 01-5.37.07c-1.79.07-3.57.52-5.36.44a17.26 17.26 0 00-2.69 0c-.89.06-1.78.07-2.68.08-1.79-.15-3.57.41-5.36.14h-.25a.8.8 0 01-.67-.92.69.69 0 01.12-.32l.75-1.09.6-1.13c.45-.73.93-1.44 1.4-2.15.89-1.47 1.6-3 2.34-4.57.36-.78.72-1.55 1.14-2.3l1.3-2.21c.66-1.58 1.48-3.08 2.22-4.61zm14.76 36.69H86a1 1 0 010-2h11.76a1 1 0 010 2zm-20.16 0H35a1 1 0 010-2h42.6a1 1 0 010 2zM98 88.14H69.31a1 1 0 010-2H98a1 1 0 010 2zm-37.25 0H35a1 1 0 010-2h25.75a1 1 0 010 2z"
    fill="#dee4f5"/>
  <circle cx="107.41" cy="106.43" r="18" fill="#dee4f5" stroke="#dee4f5" strokeLinecap="round"
          strokeLinejoin="round" strokeWidth="3"/>
  <path fill="none" stroke="#fff" strokeLinecap="round" strokeLinejoin="round" strokeWidth="3"
        d="M107.41 96.84v19.18m7.18-9.59l-7.18-9.59-7.18 9.59"/>
</svg>

const IMAGES_PATH = "/images"

const ACCEPT_IMAGE_EXTENSIONS =
  _.join(["image/gif", "image/jpeg", "image/png", "image/bmp"], ",")

class BlobDescriptor {
  public url: string = ""
  public name?: string
  public path: string = ""
  public file?: File
  public uploaded: boolean = false
  public uploading: boolean = false
  public checked: boolean = false
  public blob?: storage.Blob

  public startUpload() {
    this.uploading = true
    this.uploaded = false
  }

  public uploadCompleted() {
    this.uploading = false
    this.uploaded = true
  }
}

function fetchDirectories(storeId: string, basePath: string): Promise<storage.Blob[]> {
  return store.StoreBlobServiceV1.getBlobs()
    .storeId(storeId)
    .path(basePath)
    .directory()
    .list()
    .then(page => page.elements)
}

interface AddDirectoryButtonProps {
  onFinish?: (name: string) => void
}

function AddDirectoryButton(props: AddDirectoryButtonProps) {

  const [visible, setVisible] = useState(false)

  function onFinish(values: any) {
    if (_.isFunction(props.onFinish)) {
      props.onFinish(values["name"])
    }
    setVisible(false)
  }

  const content = (
    <Form layout="vertical" size="small" onFinish={onFinish}>
      <Form.Item
        label="文件夹名称"
        name="name"
        rules={[{ required: true, message: "请输入文件夹名称!" }]}>
        <Input/>
      </Form.Item>
      <Form.Item noStyle>
        <Button htmlType="button" onClick={() => setVisible(false)}>取消</Button>
        <Button type="primary" htmlType="submit">创建</Button>
      </Form.Item>
    </Form>
  )

  return (
    <Popover overlayClassName={classes.addDirectoryPopoverOverlay}
             placement="bottom"
             trigger="click"
             content={content}
             visible={visible}
             onVisibleChange={setVisible}
             arrowPointAtCenter>
      <Button size="small" onClick={() => setVisible(!visible)}>新建文件夹</Button>
    </Popover>
  )
}

interface UploadImageBlobProps {
  url: string
  name?: string
  expandable?: boolean
  uploading?: boolean
  uploaded?: boolean
  onDelete?: () => void
}

function UploadImageBlob(props: UploadImageBlobProps) {
  const { url, name, expandable, uploading, uploaded } = props

  return (
    <div className={classes.uploadImageBlob}>
      <div className={classNames({
        [classes.uploadImageBlobUploading]: uploading,
        [classes.uploadImageBlobUploaded]: uploaded,
        [classes.uploadImageBlobActions]: !uploading && !uploaded,
      })}>
        <DeleteFilled onClick={() => {
          if (_.isFunction(props.onDelete)) {
            props.onDelete()
          }
        }}/>
        {expandable && <ExpandAltOutlined rotate={90}/>}
        {uploading && <LoadingOutlined spin={uploading}/>}
        {uploaded && <CheckCircleOutlined/>}
      </div>
      <img src={url} alt={name}/>
    </div>
  )
}

interface UploadImageBlobButtonProps {
  onChange?: (files: File[]) => void
}

function UploadImageBlobButton(props: UploadImageBlobButtonProps) {
  const inputRef = useRef(null as unknown as HTMLInputElement)
  return (
    <div className={classes.uploadImageBlobButton}
         onClick={() => inputRef.current.click()}>
      <input type="file" ref={inputRef}
             multiple
             accept={ACCEPT_IMAGE_EXTENSIONS}
             onChange={e => {
               if (_.isFunction(props.onChange) && e.target.files) {
                 props.onChange([...e.target.files])
               }
             }}/>
      <PlusOutlined/>
    </div>
  )
}

interface UploadImageBlobsProps {
  storeId?: string
  directory?: string
  multiple?: boolean
  onUploaded?: (blobs: storage.Blob[]) => void
}

UploadImageBlobs.defaultProps = {
  multiple: true,
}

function UploadImageBlobs(props: UploadImageBlobsProps) {
  const { storeId, onUploaded, multiple } = props
  const [blobs, setBlobs] = useState([] as BlobDescriptor[])
  const [directory, setDirectory] = useState(IMAGES_PATH)
  const [uploading, setUploading] = useState(false)
  const [savedBlobs, setSavedBlobs] = useState([] as storage.Blob[])
  const maxSize = 20

  useEffect(() => {
    setDirectory(_.isUndefined(props.directory) ? IMAGES_PATH : props.directory)
  }, [props.directory])

  useEffect(() => {
    if (_.isFunction(onUploaded) && !uploading && !_.isEmpty(savedBlobs)) {
      onUploaded(savedBlobs)
    }
  }, [onUploaded, savedBlobs, uploading])

  function onAcceptedFiles(files: File[]) {
    Promise.all(_.map(_.slice(files, 0, maxSize - _.size(blobs)),
      file => new Promise<BlobDescriptor>(resolve => {
        const reader = new FileReader()
        reader.onloadend = function(e) {
          if (e.target) {
            resolve(_.assign(new BlobDescriptor(), {
              url: e.target.result,
              alt: file.name,
              path: file.name,
              file,

            }))
          }
        }
        reader.readAsDataURL(file)
      })))
      .then(newBlobs => [...blobs, ...newBlobs])
      .then(newBlobs => _.uniqWith(newBlobs, (b1, b2) => b1.url === b2.url))
      .then(setBlobs)
  }

  function uploadBlobs(resources: store.BlobResource[]) {
    return new Promise<storage.Blob[]>((resolve, reject) => {
      if (!_.isEmpty(resources)) {
        const headBlob = resources[0]
        const blob = _.find(blobs, blob => blob.path === headBlob.path) as BlobDescriptor
        blob.startUpload()
        setBlobs([...blobs])
        store.StoreBlobServiceV1
          .storeBlob({
            ...headBlob,
            path: `${directory}/${headBlob.path}`,
          })
          .then(savedBlob => {
            blob.uploadCompleted()
            setBlobs([...blobs])
            uploadBlobs(_.slice(resources, 1))
              .then(savedBlobs => resolve([savedBlob, ...savedBlobs]))
          })
          .catch(reject)
      } else {
        resolve([])
      }
    })
  }

  function onUploadBlobs() {
    const resources = _.map(blobs, blob => ({ ...blob, storeId: storeId as string }))
    if (!_.isEmpty(resources)) {
      setUploading(true)
      uploadBlobs(resources)
        .then(setSavedBlobs)
        .finally(() => setUploading(false))
    }
  }

  return (
    <div className={classes.uploadImageBlobs}>
      <header>
        <Button type="primary" size="small"
                disabled={_.isEmpty(blobs)}
                loading={uploading}
                onClick={onUploadBlobs}>提交</Button>
      </header>
      {
        !_.isEmpty(blobs) && <div className={classes.uploadImageBlobList}>
          {
            _.map(blobs, blob =>
              <UploadImageBlob key={blob.path}
                               url={blob.url}
                               name={blob.name}
                               uploaded={blob.uploaded}
                               uploading={blob.uploading}
                               onDelete={() => setBlobs(_.filter(blobs, aBlob => aBlob !== blob))}/>)
          }
          {_.size(blobs) < maxSize && multiple && <UploadImageBlobButton onChange={onAcceptedFiles}/>}
        </div>
      }
      {
        _.isEmpty(blobs) &&
        <Dropzone multiple={multiple}
                  accept={ACCEPT_IMAGE_EXTENSIONS}
                  onDropAccepted={acceptedFiles => onAcceptedFiles(acceptedFiles)}>
          {({ getRootProps, getInputProps }) => (
            <div {...getRootProps()} className={classes.uploadImageBlobDropzone}>
              <input {...getInputProps()} />
              {DragUploadImageSvg}
              <p>将图片或文件夹拖放到此处上传，或点击上传。</p>
              <Typography.Paragraph>为了保证图片的正常使用，仅支持3M以内jpg、jpeg、gif、png格式图片上传。</Typography.Paragraph>
              <Typography.Paragraph strong>支持选择多张图片上传，支持拖拽文件夹上传。</Typography.Paragraph>
            </div>
          )}
        </Dropzone>
      }
    </div>
  )
}

interface ManageImageBlobProps {
  checked?: boolean
  path: string;
  name?: string;
  url: string;
  onCheck?: (checked: boolean) => void
  onDelete?: (path: string) => void
}

function ManageImageBlob(props: ManageImageBlobProps) {
  const { path, url, name, checked, onCheck, onDelete } = props
  return (
    <div className={classes.manageImageBlob}>
      <img src={url} alt={name}/>
      <div className={classes.manageImageBlobTitle}>
        <Checkbox checked={checked}
                  onChange={e => {
                    if (_.isFunction(onCheck)) {
                      onCheck(e.target.checked)
                    }
                  }}
                  children={name}/>
      </div>
      <div className={classes.manageImageBlobActions}>
        <Button type="link" size="small">重命名</Button>
        <Divider type="vertical"/>
        <Button type="link" size="small">复制链接</Button>
        <Divider type="vertical"/>
        <Button type="link" size="small"
                onClick={() => {
                  if (_.isFunction(onDelete)) {
                    onDelete(path)
                  }
                }}>删除</Button>
      </div>
    </div>
  )
}

interface ManageImageBlobsProps {
  storeId: string
  directory: string
  onRefresh?: () => void
}

class UseBlobsProps {
  public storeId: string = ""
  public directory: string = ""
  public page: number = 1
}

function usePageBlobs({
                        storeId,
                        directory,
                        page,
                      }: UseBlobsProps) {
  const [pageBlobs, setPageBlobs] = useState(data.PageList.empty<BlobDescriptor>())
  const [loading, setLoading] = useState(false)

  const refreshPageBlobs = useCallback(() => {
    setLoading(true)
    store.StoreBlobServiceV1.getBlobs()
      .page(page)
      .storeId(storeId)
      .path(directory)
      .file()
      .list()
      .then(pageBlobs => _.assign(data.PageList.empty<BlobDescriptor>(), {
        ...pageBlobs,
        elements: _.map(pageBlobs.elements, element => _.assign(new BlobDescriptor(), element, {
          blob: element,
        })),
      }))
      .then(setPageBlobs)
      .finally(() => setLoading(false))
  }, [directory, page, storeId])

  useEffect(refreshPageBlobs, [directory, page, refreshPageBlobs, storeId])

  return {
    pageBlobs, setPageBlobs, loading, setLoading,
    refreshPageBlobs,
  }
}

function ManageImageBlobs(props: ManageImageBlobsProps) {
  const { storeId, directory } = props
  const [currentPage, setCurrentPage] = useState(1)
  const [allChecked, setAllChecked] = useState(false)
  const [allIndeterminate, setAllIndeterminate] = useState(false)

  const {
    pageBlobs, setPageBlobs,
    loading, setLoading,
    refreshPageBlobs,
  } = usePageBlobs({ storeId, directory, page: currentPage })

  useEffect(() => {
    if (loading) {
      setAllChecked(false)
      setAllIndeterminate(false)
    }
  }, [loading])

  function onAllBlobCheck(checked: boolean) {
    _.forEach(pageBlobs.elements, blob => {
      blob.checked = checked
    })
    setPageBlobs(_.assign(data.PageList.empty<BlobDescriptor>(), pageBlobs))
    setAllChecked(checked)
    setAllIndeterminate(false)
  }

  function onBlobCheck(blob: BlobDescriptor, checked: boolean) {
    blob.checked = checked
    const aAllChecked = _.chain(pageBlobs.elements)
      .map(aBlob => aBlob.checked)
      .reduce((sumChecked, aChecked) => sumChecked && aChecked, true)
      .value()

    const aAllUnchecked = _.chain(pageBlobs.elements)
      .map(aBlob => !aBlob.checked)
      .reduce((sumChecked, aChecked) => sumChecked && aChecked, true)
      .value()

    if ((!allChecked && aAllChecked) || (allChecked && !aAllChecked)) {
      setAllChecked(aAllChecked)
    }

    if ((!allIndeterminate && !aAllChecked && !aAllUnchecked) ||
      (allIndeterminate && (aAllChecked || aAllUnchecked))) {
      setAllIndeterminate(!aAllChecked && !aAllUnchecked)
    }

    setPageBlobs(_.assign(data.PageList.empty<BlobDescriptor>(), pageBlobs))
  }

  function onDeleteBlob(path: string) {
    setLoading(true)
    store.StoreBlobServiceV1.deleteBlob(storeId, path)
      .then(refreshPageBlobs)
      .finally(() => setLoading(false))
  }

  function onDeleteBlobs() {
    const paths = _.chain(pageBlobs.elements)
      .filter(blob => blob.checked)
      .map(blob => blob.path)
      .value()
    setLoading(true)
    store.StoreBlobServiceV1
      .deleteBlobs(storeId, paths)
      .then(refreshPageBlobs)
      .finally(() => setLoading(false))
  }

  return (
    <Spin spinning={loading}>
      <header>
        <Checkbox indeterminate={allIndeterminate}
                  checked={allChecked}
                  onChange={e => onAllBlobCheck(e.target.checked)}>全选</Checkbox>
        <Button type="link" size="small"
                disabled={!(allIndeterminate || allChecked)}
                onClick={onDeleteBlobs}>删除</Button>
      </header>
      {_.isEmpty(pageBlobs.elements) ? <Empty/> :
        <>
          <div className={classes.manageImageBlobs}>
            {
              _.map(pageBlobs.elements, blob =>
                <ManageImageBlob key={blob.path}
                                 url={blob.url}
                                 name={blob.name}
                                 path={blob.path}
                                 checked={blob.checked}
                                 onCheck={checked => onBlobCheck(blob, checked)}
                                 onDelete={onDeleteBlob}/>)
            }
          </div>
          <Pagination current={currentPage}
                      pageSize={pageBlobs.size}
                      total={pageBlobs.totalSize}
                      onChange={setCurrentPage}/>
        </>}
    </Spin>
  )
}

interface SelectImageBlobProps extends React.HTMLAttributes<HTMLDivElement> {
  url: string
  name?: string
}

function SelectImageBlob(props: SelectImageBlobProps) {
  const { url, name, ...restProps } = props

  const [wh, setWh] = useState([0, 0] as [number, number])

  useEffect(() => {
    const image = new Image()
    image.src = url

    function onImageLoad() {
      setWh([image.width, image.height])
    }

    image.addEventListener("load", onImageLoad)

    return () => image.removeEventListener("load", onImageLoad)
  }, [url])

  return (
    <div className={classes.selectImageBlob} {...restProps}>
      <img src={url} alt={name}/>
      <footer>{`${wh[0]}x${wh[1]}`}</footer>
    </div>
  )
}

interface SelectImageBlobsProps {
  storeId: string
  directory: string
  onSelect?: (blob: storage.Blob) => void
}

function SelectImageBlobs(props: SelectImageBlobsProps) {
  const { storeId, directory, onSelect } = props
  const [currentPage, setCurrentPage] = useState(1)
  const { pageBlobs, loading } = usePageBlobs({ storeId, directory, page: currentPage })

  return <div className={classes.selectImageBlobs}>
    <Spin spinning={loading}>
      {_.isEmpty(pageBlobs.elements) ? <Empty/> :
        <>
          <div>
            {
              _.map(pageBlobs.elements, blob =>
                <SelectImageBlob key={blob.path}
                                 url={blob.url}
                                 name={blob.name}
                                 onClick={() => {
                                   if (_.isFunction(onSelect) && blob.blob) {
                                     onSelect(blob.blob)
                                   }
                                 }}/>)
            }
          </div>
          <Pagination current={currentPage}
                      pageSize={pageBlobs.size}
                      total={pageBlobs.totalSize}
                      onChange={setCurrentPage}/>
        </>}
    </Spin>
  </div>
}

interface ImageBlobsProps {
  upload?: UploadImageBlobsProps
  selectable?: boolean
  onSelect?: (blobs: storage.Blob[]) => void
}

export default function ImageBlobs(props: ImageBlobsProps) {
  const { upload, selectable, onSelect } = props
  const { storeId = "" } = useParams()
  const [directories, setDirectories] = useState([] as storage.Blob[])
  const [directoriesLoading, setDirectoriesLoading] = useState(false)
  const [currentDirectory, setCurrentDirectory] = useState(IMAGES_PATH)
  const [uploadable, setUploadable] = useState(false)

  // Refresh directories.
  const refreshDirectories = useCallback(() => {
    setDirectoriesLoading(true)
    fetchDirectories(storeId, IMAGES_PATH)
      .then(setDirectories)
      .finally(() => setDirectoriesLoading(false))
  }, [storeId])

  useEffect(() => refreshDirectories(), [refreshDirectories, storeId])

  function onAddDirectory(newDirectory: string) {
    setDirectoriesLoading(prevState => prevState && true)
    store.StoreBlobServiceV1.storeBlob({
      storeId, path: `${IMAGES_PATH}/${newDirectory}`,
    }).finally(refreshDirectories)
  }

  function directoryTreeNodes(blobs: storage.Blob[]) {

    interface NodeTitleProps {
      path: string
      children?: string
    }

    function NodeTitle(props: NodeTitleProps) {
      const { children, path } = props
      const [editable, setEditable] = useState(false)

      return (
        <div className={classNames({
          [classes.directoryTreeNode]: !editable,
          [classes.directoryTreeNodeEditable]: editable,
        })}>
          {!editable && <>
            {children}
            <span>
              {path === currentDirectory && <EditOutlined
                onClick={() => setEditable(!editable)}/>}
              {path === currentDirectory && <DeleteRowOutlined/>}
            </span>
          </>}
          {editable && <>
            <Input autoFocus size="small" defaultValue={children}
                   onPressEnter={() => setEditable(!editable)}/>
            <CheckOutlined onClick={() => setEditable(!editable)}/>
            <RollbackOutlined onClick={() => setEditable(!editable)}/>
          </>}
        </div>
      )
    }

    return [{
      key: IMAGES_PATH,
      title: <div className={classes.directoryTreeNode}>默认文件夹</div>,
    }, ..._.map(blobs, directory => ({
      key: directory.path,
      title: <NodeTitle path={directory.path} children={directory.name}/>,
    }))]
  }

  function onUploaded(blobs: storage.Blob[]) {
    setUploadable(false)
    if (_.isFunction(upload?.onUploaded)) {
      upload?.onUploaded(blobs)
    }

    if (_.isFunction(onSelect)) {
      onSelect(blobs)
    }
  }

  return (
    <div className={classes.imageBlobs}>
      <div className={classes.directories}>
        <Spin spinning={directoriesLoading}>
          <Button onClick={() => setUploadable(!uploadable)}
                  type="primary"
                  size="small">{uploadable ? "取消上传" : "上传图片"}</Button>
          <AddDirectoryButton onFinish={onAddDirectory}/>
          <DirectoryTree
            blockNode
            className={classes.directoryTree}
            selectedKeys={[currentDirectory]}
            showIcon={false}
            onSelect={(selectedKeys: any[], { selectedNodes }) =>
              setCurrentDirectory(selectedNodes[0].key as string)}
            treeData={directoryTreeNodes(directories)}/>
        </Spin>
      </div>
      <div className={classes.imageBlobsContent}>
        {!uploadable && !selectable &&
        <ManageImageBlobs storeId={storeId}
                          directory={currentDirectory}/>}
        {!uploadable && selectable &&
        <SelectImageBlobs storeId={storeId}
                          onSelect={blob => {
                            if (_.isFunction(onSelect)) {
                              onSelect([blob])
                            }
                          }}
                          directory={currentDirectory}/>}
        {uploadable && <UploadImageBlobs storeId={storeId}
                                         multiple={props.upload?.multiple}
                                         directory={currentDirectory}
                                         onUploaded={onUploaded}/>}
      </div>
    </div>
  )
}


interface ImageBlobsPopoverProps extends ImageBlobsProps {
  children?: React.ReactNode
  visible?: boolean
  offset?: [number, number]
  onVisibleChange?: (visible: boolean) => void
}

ImageBlobsPopover.defaultProps = {
  selectable: true,
}

export function ImageBlobsPopover(props: ImageBlobsPopoverProps) {
  const { selectable, upload, onSelect, onVisibleChange, visible } = props

  const offset = _.isUndefined(props.offset) ? [0, 0] : props.offset

  return (
    <Popover trigger="click"
             placement="bottom"
             mouseEnterDelay={0}
             className={classes.imageBlobsPopover}
             overlayClassName={classes.imageBlobsPopoverOverlay}
             content={<ImageBlobs {...{ selectable, onSelect, upload }}/>}
             visible={visible}
             onVisibleChange={onVisibleChange}
             align={{
               offset,
               targetOffset: [0, 10],
               useCssBottom: false,
               useCssRight: false,
             }}
    />
  )
}
